spring-framework-4-reference

15.4 JDO

Spring 支持标准的 JDO 2.0 和 2.1 API 的数据访问策略,按照与 Hibernate 同样的支持方式。相应的集成类驻留在org.springframework.orm.jdo 包。

15.4.1 PersistenceManagerFactory setup 设置

Spring提供 LocalPersistenceManagerFactoryBean 类允许您在一个 Spring 应用上下文定义了一个局部的 JDO 的PersistenceManagerFactory

<beans>

    <bean id="myPmf" class="org.springframework.orm.jdo.LocalPersistenceManagerFactoryBean">
        <property name="configLocation" value="classpath:kodo.properties"/>
    </bean>

</beans>

另外,你可以通过一个 PersistenceManagerFactory实现类的的实例化来设置 PersistenceManagerFactory。一个 JDO 的PersistenceManagerFactory实现类遵循 JavaBean 模式,就像一个JDBC DataSource 的实现类,这是在 Spring 里配置使用是非常合适的。这种设置方式通常支持一个 Spring 定义的JDBC DataSource,传递给 connectionFactory。例如,对于开源的 JDO 实现DataNucleus(原名 JPOX )(http://www.datanucleus.org/),下面是PersistenceManagerFactory实现的 XML 配置:

<beans>

 <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
    <property name="driverClassName" value="${jdbc.driverClassName}"/>
    <property name="url" value="${jdbc.url}"/>
    <property name="username" value="${jdbc.username}"/>
    <property name="password" value="${jdbc.password}"/>
 </bean>

 <bean id="myPmf" class="org.datanucleus.jdo.JDOPersistenceManagerFactory" destroy-method="close">
    <property name="connectionFactory" ref="dataSource"/>
    <property name="nontransactionalRead" value="true"/>
 </bean>

</beans>

也可以在 Java EE 应用服务的 JNDI 环境中 设置 JDO PersistenceManagerFactory,通常是通过 JCA 连接器提供包含 JDO 的实现。Spring 的标准中 JndiObjectFactoryBean<jee:jndi-lookup>可以用来检索和暴露比如PersistenceManagerFactory。然而,在 EJB 上下文 之外,没有真正的存在于在 JNDI 中保持 PersistenceManagerFactory :只选择这样的一个设置是一个很好的理由。请参见15.3.6“比较容器管理和本地定义的资源”讨论;那里的论点适用于JDO。

15.4.2 Implementing DAOs based on the plain JDO API 基于平常 JDO API 的 DAO 的实现

利用注入的 PersistenceManagerFactory,也可以直接利用平常的JDO API来写 DAO,而无需 Spring 的依赖。以下是相应的 DAO 实现的一个例子:

public class ProductDaoImpl implements ProductDao {

    private PersistenceManagerFactory persistenceManagerFactory;

    public void setPersistenceManagerFactory(PersistenceManagerFactory pmf) {
        this.persistenceManagerFactory = pmf;
    }

    public Collection loadProductsByCategory(String category) {
        PersistenceManager pm = this.persistenceManagerFactory.getPersistenceManager();
        try {
            Query query = pm.newQuery(Product.class, "category = pCategory");
            query.declareParameters("String pCategory");
            return query.execute(category);
        }
        finally {
            pm.close();
        }
    }
}

因为上面的 DAO 依赖注入模式,它适合在 Spring 容器中,就像在Spring 的 JdoTemplate 中编码:

<beans>

    <bean id="myProductDao" class="product.ProductDaoImpl">
        <property name="persistenceManagerFactory" ref="myPmf"/>
    </bean>

</beans>

这样的 DAO 主要的问题是,他们总是从工厂获得一个新的PersistenceManager。为了访问 Spring 管理的事务 PersistenceManager,需要定义一个TransactionAwarePersistenceManagerFactoryProxy(包含在Spring 中)在你的目标 PersistenceManagerFactory 面前,然后传递一个那个代理的引用到你的 DAO,如下面的示例:

<beans>

    <bean id="myPmfProxy"
            class="org.springframework.orm.jdo.TransactionAwarePersistenceManagerFactoryProxy">
        <property name="targetPersistenceManagerFactory" ref="myPmf"/>
    </bean>

    <bean id="myProductDao" class="product.ProductDaoImpl">
        <property name="persistenceManagerFactory" ref="myPmfProxy"/>
    </bean>

</beans>

你的数据访问代码将收到一个来自 PersistenceManagerFactory.getPersistenceManager()调用的事务性的PersistenceManager(如果有)的方法。后者的方法的调用会通过代理,在从从工厂获得一个新的之前它首先检查当前事务性的PersistenceManager。由于 事务性的PersistenceManager,任何 close() 的调用将会被忽略。

如果你的数据访问代码总是运行在一个活跃的事务中(或至少与活跃的事务同步),它会安全的忽略 PersistenceManager.close()的调用。这样整个finally的块,可以让你的 DAO 实现更加简洁:

public class ProductDaoImpl implements ProductDao {

    private PersistenceManagerFactory persistenceManagerFactory;

    public void setPersistenceManagerFactory(PersistenceManagerFactory pmf) {
        this.persistenceManagerFactory = pmf;
    }

    public Collection loadProductsByCategory(String category) {
        PersistenceManager pm = this.persistenceManagerFactory.getPersistenceManager();
        Query query = pm.newQuery(Product.class, "category = pCategory");
        query.declareParameters("String pCategory");
        return query.execute(category);
    }
}

由于这样 DAO 依来活动的事务,所有建议您通过关闭TransactionAwarePersistenceManagerFactoryProxyallowCreate 标签来强制激活事务:

<beans>

    <bean id="myPmfProxy"
            class="org.springframework.orm.jdo.TransactionAwarePersistenceManagerFactoryProxy">
        <property name="targetPersistenceManagerFactory" ref="myPmf"/>
        <property name="allowCreate" value="false"/>
    </bean>

    <bean id="myProductDao" class="product.ProductDaoImpl">
        <property name="persistenceManagerFactory" ref="myPmfProxy"/>
    </bean>

</beans>

这种 DAO 风格的主要优势是,它只依赖于 JDO API;不需要引进任何的Spring 类。从非侵入性的角度来说更吸引人,并且对于 JDO 开发人员来说可能会觉得更自然。

然而,DAO 抛出平常的 JDOException(未检查的,因此不需要声明或捕获),这意味着调用者只能将异常当做是致命的,除非你想依靠 JDO 的异常结构。捕捉乐观锁失败等特殊原因是不可能,除非把调用者与实现策略相关联。取消这交易可能会更容易受应用程序接受,因为基于 JDO 和/或 不需要任何特殊的异常处理。

总之,你可以根据平常的 JDO API 生产 DAO ,他们仍然可以参与 Spring管理事务。这策略会可能会吸引你如果你已经熟悉了 JDO。然而,这样的DAO 抛出平常的 JDOException,您必须显式地转换为 Spring 的DataAccessException(如果需要)。

15.4.3 Transaction management 事务管理

如果你还没有看过 12.5. Declarative transaction management 声明式事务管理强烈建议你看下,获取更多Spring 声明式事务的支持

执行服务的事务操作,使用 Spring 常见的声明式事务功能,举例:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:aop="http://www.springframework.org/schema/aop"
    xmlns:tx="http://www.springframework.org/schema/tx"
    xsi:schemaLocation="
        http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/tx
        http://www.springframework.org/schema/tx/spring-tx.xsd
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop.xsd">

    <bean id="myTxManager" class="org.springframework.orm.jdo.JdoTransactionManager">
        <property name="persistenceManagerFactory" ref="myPmf"/>
    </bean>

    <bean id="myProductService" class="product.ProductServiceImpl">
        <property name="productDao" ref="myProductDao"/>
    </bean>

    <tx:advice id="txAdvice" transaction-manager="txManager">
        <tx:attributes>
            <tx:method name="increasePrice*" propagation="REQUIRED"/>
            <tx:method name="someOtherBusinessMethod" propagation="REQUIRES_NEW"/>
            <tx:method name="*" propagation="SUPPORTS" read-only="true"/>
        </tx:attributes>
    </tx:advice>

    <aop:config>
        <aop:pointcut id="productServiceMethods"
                expression="execution(* product.ProductService.*(..))"/>
        <aop:advisor advice-ref="txAdvice" pointcut-ref="productServiceMethods"/>
    </aop:config>

</beans>

JDO 需要一个活动的事务来修改持久化的对象。非事务性的刷新概念并不存在于 JDO,相对于 Hibernate。为此,你需要为特定的环境设置选择的JDO 的实现。具体来说,你需要设置明确的 JTA 同步,来检测一个活跃的JTA 事务本身。这对于 Spring 的 JdoTransactionManager执行的本地事务来说是没有必要的,但有必要参与 JTA 事务,不管是由 Spring JtaTransactionManager驱动 还是 EJB CMT 和普通的 JTA。

JdoTransactionManager能够使 JDO 事务 JDBC 访问代码f访问同一个JDBCDataSource,提供注册的 JdoDialect 支持底层的 JDBC Connection检索。这是默认情况下基于JDBC 的 JDO 2.0实现

15.4.4 JdoDialect

作为一个高级功能,JdoTemplateJdoTransactionManager支持自定义JdoDialect可以传递到JdoDialect的 bean 属性。在这个场景中,DAO 不接受 PersistenceManagerFactory的引用,而是一个完整的JdoTemplate实例(例如,传递到JdoDaoSupport的属性JdoTemplate 中)。使用JdoDialect实现,您可以启用 Spring 的高级特性支持,通常特定于供应商的方式:

  • 应用于特定的事务语义,如自定义隔离级别或事务超时
  • 检索事务性的 JDBC Connection,用来暴露基于 JDBC 的 DAO
  • 应用查询超时,自动从 Spring 管理事务超时进行计算
  • 及时刷新 PersistenceManager,使事务变化对于基于 JDBC 的数据访问代码可见
  • JDOExceptions 向 Spring DataAccessExceptions的高级转换

查看 JdoDialect 的 javadocs 获取更多如果使用 Spring JDO 的细节